/*
 ============================================================================
 Name		: TetrisGameAppView.cpp
 Author	  : Vladimir Hozjan
 Copyright   : Copyright (C) 2008 Vladimir Hozjan <vladimir.hozjan@gmail.com>
 Description : Application view implementation
 ============================================================================
 */

// INCLUDE FILES
#include <coemain.h>
#include <eikenv.h>
#include <eikdef.h>
#include <e32base.h>
#include <gulfont.h>
#include "TetrisGameAppView.h"
#include "Tetris.h"
#include "TetrisGame.mbg"

_LIT(KTxtNext,"Next");
_LIT(KTxtScore,"Score");
_LIT(KTxtLevel,"Level");
_LIT(KTxtLines,"Lines");
_LIT(KTxtGameOver,"GAME OVER");
_LIT(KIntFormat,"%d");
_LIT(KDblFormat,"%.0f");


// ============================ STATIC VARIABLES ===============================
static TRgb sShapeColors[] =
	{
			TRgb(255, 255, 255),
			TRgb(204, 102, 102),
			TRgb(102, 204, 102),
			TRgb(102, 102, 204),
			TRgb(204, 204, 102),
			TRgb(204, 102, 204),
			TRgb(102, 204, 204),
			TRgb(218, 170, 0)
	};

static TRgb sShapeColorsLight[] =
	{
			TRgb(255, 255, 255),
			TRgb(248, 159, 171),
			TRgb(121, 252, 121),
			TRgb(121, 121, 252),
			TRgb(252, 252, 121),
			TRgb(252, 121, 252),
			TRgb(121, 252, 252),
			TRgb(252, 198, 0)
	};

static TRgb sShapeColorsDark[] =
	{
			TRgb(255, 255, 255),
			TRgb(128,  59,  59),
			TRgb( 59, 128,  59),
			TRgb( 59,  59, 128),
			TRgb(128, 128,  59),
			TRgb(128,  59, 128),
			TRgb( 59, 128, 128),
			TRgb(128,  98, 0)
	};

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CTetrisGameAppView::NewL()
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CTetrisGameAppView* CTetrisGameAppView::NewL(const TRect& aRect)
	{
	CTetrisGameAppView* self = CTetrisGameAppView::NewLC(aRect);
	CleanupStack::Pop(self);
	return self;
	}

// -----------------------------------------------------------------------------
// CTetrisGameAppView::NewLC()
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CTetrisGameAppView* CTetrisGameAppView::NewLC(const TRect& aRect)
	{
	CTetrisGameAppView* self = new (ELeave) CTetrisGameAppView;
	CleanupStack::PushL(self);
	self->ConstructL(aRect);
	return self;
	}

// -----------------------------------------------------------------------------
// CTetrisGameAppView::ConstructL()
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CTetrisGameAppView::ConstructL(const TRect& aRect)
	{
	// Create a window for this application view
	CreateWindowL();

	// Set the windows size
	SetRect(aRect);

	// Activate the window, which makes it ready to be drawn
	ActivateL();
	
    iTetris = NULL;
	}

// -----------------------------------------------------------------------------
// CTetrisGameAppView::CTetrisGameAppView()
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CTetrisGameAppView::CTetrisGameAppView():
iMaxRow(20),
iMaxCol(10)
	{
	// No implementation required
	}

// -----------------------------------------------------------------------------
// CTetrisGameAppView::~CTetrisGameAppView()
// Destructor.
// -----------------------------------------------------------------------------
//
CTetrisGameAppView::~CTetrisGameAppView()
	{
    ReleaseBackBuffer();
	}

// -----------------------------------------------------------------------------
// CTetrisGameAppView::Draw()
// Draws the display.
// -----------------------------------------------------------------------------
//
void CTetrisGameAppView::Draw(const TRect& /*aRect*/) const
	{
    CWindowGc& gc = SystemGc();
    
    // Copy backbuffer to the screen
    gc.BitBlt(TPoint(0, 0), iBackBuffer);
	}

// -----------------------------------------------------------------------------
// CTetrisGameAppView::SizeChanged()
// Called by framework when the view size is changed.
// -----------------------------------------------------------------------------
//
void CTetrisGameAppView::SizeChanged()
	{
    // Delete back buffer and create a new one with new size
    ReleaseBackBuffer();
    CreateBackBufferL();
    DrawToBackBuffer();
	}

// -----------------------------------------------------------------------------
// CTetrisGameAppView::HandlePointerEventL()
// Called by framework to handle pointer touch events.
// Note: although this method is compatible with earlier SDKs, 
// it will not be called in SDKs without Touch support.
// -----------------------------------------------------------------------------
//
void CTetrisGameAppView::HandlePointerEventL(const TPointerEvent& aPointerEvent)
	{

	// Call base class HandlePointerEventL()
	CCoeControl::HandlePointerEventL(aPointerEvent);
	}

void CTetrisGameAppView::CreateBackBufferL()
    {
    // Create back buffer bitmap
    iBackBuffer = new (ELeave) CFbsBitmap;
    
    User::LeaveIfError( iBackBuffer->Create(Size(), iEikonEnv->DefaultDisplayMode()));
    
    // Create back buffer graphics context
    iBackBufferDevice = CFbsBitmapDevice::NewL(iBackBuffer);
    User::LeaveIfError(iBackBufferDevice->CreateContext(iBackBufferContext));
    iBackBufferContext->SetPenStyle(CGraphicsContext::ESolidPen);
 
    iBackBufferSize = iBackBuffer->SizeInPixels();
	iBlockSize = (iBackBufferSize.iHeight - 1) / (iMaxRow);
    iEmptySpace = iBackBufferSize.iHeight - (iBlockSize * (iMaxRow));
    }
 
void CTetrisGameAppView::ReleaseBackBuffer()
    {
    // Release double buffering classes
    if (iBackBufferContext)
        {
        delete iBackBufferContext;
        iBackBufferContext = NULL;
        }
    if (iBackBufferDevice)
        {
        delete iBackBufferDevice;
        iBackBufferDevice = NULL;
        }
    if (iBackBuffer)
        {
        delete iBackBuffer;
        iBackBuffer = NULL;
        }
    iBackBufferSize = TSize(0, 0);
    }


#include <eikappui.h>
#include <eikapp.h>

const TDesC& addDriveLetter(const TDesC& aFilename, TBuf<256>& aContainer)
	{
	TFileName appName;

	appName = CEikonEnv::Static()->EikAppUi()->Application()->AppFullName();
	TParse appFile;
	appFile.Set(appName, NULL, NULL);
	TBuf<2> driveName(appFile.Drive());

	aContainer.Append(driveName);
	aContainer.Append(aFilename);
	return aContainer;
	}


void CTetrisGameAppView::DrawToBackBuffer()
    {
	if (!iBackBufferContext)
		{
		return;
		}

	iBackBufferContext->Clear();

	// Remember to draw to iBackBufferContext buffered graphic contex
	if (iTetris == 0)
		{

		_LIT (KBitmapfile,"\\RESOURCE\\APPS\\TetrisGame.mbm");
		CFbsBitmap* bitmap = new (ELeave) CFbsBitmap();
        TBuf<256> buf;
        
		bitmap->Load(addDriveLetter(KBitmapfile, buf), EMbmTetrisgameStartpage);
		TPoint pos((iBackBufferSize.iWidth - 240) / 2, (iBackBufferSize.iHeight	- 240) / 2);
		iBackBufferContext->BitBlt(pos, bitmap);

		return;
		}
	
	// Draw border
    iBackBufferContext->SetPenSize(TSize(0, 0));
    iBackBufferContext->SetBrushStyle( CGraphicsContext::ESolidBrush );
    iBackBufferContext->SetBrushColor(TRgb(96,  32,  32));
	
    TRect rectDark(0, 0, iBlockSize * iMaxCol + 2*iEmptySpace, iBackBufferSize.iHeight);
    iBackBufferContext->DrawRect(rectDark); 
    
    iBackBufferContext->SetBrushColor(TRgb(255,255,255));
    TRect rectWhite(iEmptySpace, 0, iBlockSize * iMaxCol + iEmptySpace, iBackBufferSize.iHeight - iEmptySpace);
    iBackBufferContext->DrawRect(rectWhite); 

	// Draw matrix	
	TInt i, j;
	for (i = 0; i < iMaxRow; i++)
		{
		for (j = 0; j < iMaxCol; j++)
			{
			TUint16 idx = iTetris->getCell(i, j);
			drawRect(j, iMaxRow - 1 - i, idx);
			}
		}

	
	// Draw right panel
	// "Next" text
	TInt xOffset = 3*iEmptySpace + iBlockSize*iMaxCol;
	TBuf<20> tmp;

	iBackBufferContext->SetPenSize(TSize(1,1));

	iBackBufferContext->UseFont(iEikonEnv->NormalFont());
	iBackBufferContext->SetPenColor(TRgb(0,0,200));
	iBackBufferContext->DrawText(KTxtNext,TPoint(xOffset, 20));
    iBackBufferContext->DrawText(KTxtScore,TPoint(xOffset, 80));
    iBackBufferContext->DrawText(KTxtLevel,TPoint(xOffset, 120));
    iBackBufferContext->DrawText(KTxtLines,TPoint(xOffset, 160));
	iBackBufferContext->DiscardFont();

	
    iBackBufferContext->UseFont(iEikonEnv->NormalFont());
	iBackBufferContext->SetPenColor(TRgb(0,0,100));
	tmp.Format(KDblFormat, iTetris->getScore());
    iBackBufferContext->DrawText(tmp,TPoint(xOffset, 100));
    tmp.Format(KIntFormat, iTetris->getLevel()+1);
    iBackBufferContext->DrawText(tmp,TPoint(xOffset, 140));
    tmp.Format(KIntFormat, iTetris->getNumberOfLines());
	iBackBufferContext->SetPenColor(TRgb(0,0,100));
    iBackBufferContext->DrawText(tmp,TPoint(xOffset, 180));
	iBackBufferContext->DiscardFont();
    
    
	// Draw next element
    TUint16 elSize = iTetris->getDimensionOfNextElement();
	for (i = 0; i < elSize; i++)
		{
		for (j = 0; j < elSize; j++)
			{
			TUint16 idx = iTetris->getCellOfNextElement(i, j);
			drawRectNext(j, elSize - 1 - i, idx);
			}
		}
	
	if (iTetris->isGameOver())
		{
	    iBackBufferContext->SetBrushColor(TRgb(255,255,255));
	    iBackBufferContext->SetBrushStyle( CGraphicsContext::ESolidBrush);
	    TRect rectWhite(iEmptySpace, iBackBufferSize.iHeight - iEmptySpace - 30, iBlockSize * iMaxCol + iEmptySpace, iBackBufferSize.iHeight - iEmptySpace);
	    iBackBufferContext->DrawRect(rectWhite); 
	    
		iBackBufferContext->UseFont(iEikonEnv->NormalFont());
		iBackBufferContext->SetPenColor(TRgb(255,0,0));
		TPoint pnt(iMaxCol*iBlockSize/2 - 35, iBackBufferSize.iHeight - iEmptySpace - 5);
	    iBackBufferContext->DrawText(KTxtGameOver, pnt);
		iBackBufferContext->DiscardFont();
		}
	}

void CTetrisGameAppView::drawRect(TInt x, TInt y, TUint16 color)
	{
	if (color == 0 || color > 7)
		{
		return;
		}

	TInt linewidth = 1;
	
	TInt startx = iBlockSize * x + iEmptySpace;
	TInt starty = iBlockSize * y ;

	TInt endx = iBlockSize * (x + 1) + iEmptySpace;
	TInt endy = iBlockSize * (y + 1);

    iBackBufferContext->SetBrushStyle( CGraphicsContext::ESolidBrush );
    iBackBufferContext->SetPenStyle( CGraphicsContext::ESolidPen );
    iBackBufferContext->SetPenSize(TSize(linewidth, linewidth));
    
    iBackBufferContext->SetPenColor(sShapeColorsLight[color]);
	
    iBackBufferContext->DrawLine(TPoint(startx, starty),TPoint(endx - linewidth, starty ));
    iBackBufferContext->DrawLine(TPoint(startx, starty),TPoint(startx , endy - linewidth));

    iBackBufferContext->SetPenColor(sShapeColorsDark[color]);

    iBackBufferContext->DrawLine(TPoint(endx - linewidth, endy - linewidth),TPoint(startx, endy - linewidth ));
    iBackBufferContext->DrawLine(TPoint(endx - linewidth, endy - linewidth),TPoint(endx - linewidth, starty ));

    iBackBufferContext->SetPenSize(TSize(0, 0));
    iBackBufferContext->SetBrushColor( sShapeColors[color] );
    TRect rect(startx + linewidth, starty + linewidth, startx + iBlockSize - (linewidth), starty + iBlockSize - (linewidth));
    iBackBufferContext->DrawRect(rect);
    
    iBackBufferContext->SetBrushColor(sShapeColors[0]);
    iBackBufferContext->SetBrushStyle( CGraphicsContext::ENullBrush );
    iBackBufferContext->SetPenStyle( CGraphicsContext::ENullPen );

	}

void CTetrisGameAppView::drawRectNext(TInt x, TInt y, TUint16 color)
	{
	if (color == 0 || color > 7)
		{
		return;
		}

	TInt linewidth = 1;
	TInt xOffset = 3*iEmptySpace + iBlockSize*iMaxCol;
	TInt yOffset = 25;
		
	TInt startx = iBlockSize * x + xOffset;
	TInt starty = iBlockSize * y + yOffset;

	TInt endx = iBlockSize * (x + 1) + xOffset;
	TInt endy = iBlockSize * (y + 1) + yOffset;

    iBackBufferContext->SetBrushStyle( CGraphicsContext::ESolidBrush );
    iBackBufferContext->SetPenStyle( CGraphicsContext::ESolidPen );
    iBackBufferContext->SetPenSize(TSize(linewidth, linewidth));
    
    iBackBufferContext->SetPenColor(sShapeColorsLight[color]);
	
    iBackBufferContext->DrawLine(TPoint(startx, starty),TPoint(endx - linewidth, starty ));
    iBackBufferContext->DrawLine(TPoint(startx, starty),TPoint(startx , endy - linewidth));

    iBackBufferContext->SetPenColor(sShapeColorsDark[color]);

    iBackBufferContext->DrawLine(TPoint(endx - linewidth, endy - linewidth),TPoint(startx, endy - linewidth ));
    iBackBufferContext->DrawLine(TPoint(endx - linewidth, endy - linewidth),TPoint(endx - linewidth, starty ));

    iBackBufferContext->SetPenSize(TSize(0, 0));
    iBackBufferContext->SetBrushColor( sShapeColors[color] );
    TRect rect(startx + linewidth, starty + linewidth, startx + iBlockSize - (linewidth), starty + iBlockSize - (linewidth));
    iBackBufferContext->DrawRect(rect);
    
    iBackBufferContext->SetBrushColor(sShapeColors[0]);
    iBackBufferContext->SetBrushStyle( CGraphicsContext::ENullBrush );
    iBackBufferContext->SetPenStyle( CGraphicsContext::ENullPen );

	}

// ---------------------------------------------------------
// CTetrisGameAppView::OfferKeyEventL()
// Notify key events to the editor.
// ( other items were commented in a header ).
// ---------------------------------------------------------
//
TKeyResponse CTetrisGameAppView::OfferKeyEventL( const TKeyEvent& aKeyEvent, TEventCode aType )
    {
	TKeyResponse retval = EKeyWasNotConsumed;
	/**
	 * If key pressed TEventCode allways are send in next order EEventKeyDown, EEventKey, EEventKeyUp 
	 */
	if (aType == EEventKey)
		{
		/**
		 * Codes defined in TKeyCode
		 */
		/*
		TKeyCode code;
		TEventModifier mod;
		*/
		if (iTetris)
			{
			retval = EKeyWasConsumed;
			switch (aKeyEvent.iCode)
				{
				case EKeyUpArrow: 
					iTetris->rotateRight();
					break;
				case EKeyDownArrow:
					iTetris->moveDown();
					break;
				case EKeyLeftArrow:
					iTetris->moveLeft();
					break;
				case EKeyRightArrow:
					iTetris->moveRight();
					break;
				case EKeyDevice3:
					iTetris->drop();
					break;
				default:
					retval = EKeyWasNotConsumed;
				}
			}
		}
	return retval;
	}

// End of File
